#pragma once

#include <iostream>
#include <functional>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

class Connection
{
public:
    using ptr = std::shared_ptr<Connection>;
public:
    Connection(int sockfd, uint32_t events, std::shared_ptr<TcpServer> reactor): 
        _sockfd(sockfd), _events(events), _reactor(reactor) {}

    void registerCallback(const func_t& recver, const func_t& sender, const func_t& excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }

    void addInBuffer(const std::string& data)
    {
        _inbuffer.append(data);
    }
    void addOutBuffer(const std::string& data)
    {
        _outbuffer.append(data);
    }
    bool outBufferEmpty() const
    {
        return _outbuffer.empty();
    }

    int getFd() const
    {
        return _sockfd;
    }

    void setEvents(uint32_t events)
    {
        _events = events;
    }

    uint32_t getEvents() const
    {
        return _events;
    }

    std::string& getInBuffer()
    {
        return _inbuffer;
    }
    std::string& getOutBuffer()
    {
        return _outbuffer;
    }

    void setClientAddr(const struct sockaddr_in& addr)
    {
        _client_addr = addr;
    }
    const struct sockaddr_in& getClientAddr() const
    {
        return _client_addr;
    }

    void closeFd()
    {
        ::close(_sockfd);
    }

    // callback functions interface
    const func_t& getRecverCallback() const
    {
        return _recver;
    }
    const func_t& getSenderCallback() const
    {
        return _sender;
    }
    const func_t& getExcepterCallback() const
    {
        return _excepter;
    }

    // reactor interface
    auto getReactor() const -> std::shared_ptr<TcpServer>
    {
        return _reactor;
    }
    
private:
    int _sockfd;
    std::string _inbuffer;  // for resv
    std::string _outbuffer;  // for send

    // care events
    uint32_t _events;  // for epoll_ctl

    // maintain the ip and port of the client connected
    struct sockaddr_in _client_addr;

    // define the callback function for the specific connection
    func_t _recver;
    func_t _sender;
    func_t _excepter;

    // reactor (server pointer)
    std::shared_ptr<TcpServer> _reactor;
};

class ConnectionFactory
{
public:
    static std::shared_ptr<Connection> buildListenConnection(int listenfd, uint32_t events, 
                                     std::shared_ptr<TcpServer> reactor, const func_t& recver)
    {
        auto conn = std::make_shared<Connection>(listenfd, events, reactor);
        conn->registerCallback(recver, nullptr, nullptr);
        return conn;
    }

    static std::shared_ptr<Connection> buildNormalConnection(int sockfd, uint32_t events, 
                                     std::shared_ptr<TcpServer> reactor, 
                                     const func_t& recver, const func_t& sender, const func_t& excepter)
    {
        auto conn = std::make_shared<Connection>(sockfd, events, reactor);
        conn->registerCallback(recver, sender, excepter);
        return conn;
    }
};